package service

import (
	"context"
	"errors"
	"github.com/kuangshp/go-utils/k"
	"github.com/shopspring/decimal"
	"gorm.io/gorm"
	"insighttracking/query/dao"
	"insighttracking/query/model"
	"sync"
)

type _spuInventoryService struct{}

var lock1 sync.Mutex
var lock2 sync.Mutex

func (s _spuInventoryService) IncrInventoryApi(ctx context.Context, tx *dao.QueryTx, enterpriseId int64, warehouseID, spuId int64, changeCount decimal.Decimal) error {
	lock1.Lock()
	defer lock1.Unlock()
	inventoryEntity, err := dao.SpuInventoryEntity.WithContext(ctx).Where(
		dao.SpuInventoryEntity.EnterpriseID.Eq(enterpriseId),
		dao.SpuInventoryEntity.SpuID.Eq(spuId),
		dao.SpuInventoryEntity.WarehouseID.Eq(warehouseID),
	).First()
	if errors.Is(err, gorm.ErrRecordNotFound) {
		// 创建
		if err = tx.SpuInventoryEntity.WithContext(ctx).Create(&model.SpuInventoryEntity{
			EnterpriseID:   enterpriseId, // 关联到enterprise表主键id
			SpuID:          spuId,        // 商品id,关联打牌spu表主键id
			WarehouseID:    warehouseID,  // 仓库id,关联到warehouse表主键id
			InventoryCount: changeCount,  // 当前仓库库存数量
		}); err != nil {
			return err
		}
	} else {
		// 更新
		lastInventoryCount := inventoryEntity.InventoryCount.Add(changeCount)
		if _, err = tx.SpuInventoryEntity.WithContext(ctx).Where(dao.SpuInventoryEntity.ID.Eq(inventoryEntity.ID)).UpdateSimple(
			dao.SpuInventoryEntity.InventoryCount.Value(lastInventoryCount),
		); err != nil {
			return err
		}
	}
	return nil
}

func (s _spuInventoryService) ReduceInventoryApi(ctx context.Context, tx *dao.QueryTx, enterpriseId int64, warehouseID, spuId int64, changeCount decimal.Decimal) error {
	lock2.Lock()
	defer lock2.Unlock()
	inventoryEntity, err := dao.SpuInventoryEntity.WithContext(ctx).Where(
		dao.SpuInventoryEntity.EnterpriseID.Eq(enterpriseId),
		dao.SpuInventoryEntity.SpuID.Eq(spuId),
		dao.SpuInventoryEntity.WarehouseID.Eq(warehouseID),
	).First()
	if errors.Is(err, gorm.ErrRecordNotFound) {
		return errors.New("当前仓库没有该商品")
	}
	if inventoryEntity.InventoryCount.LessThan(changeCount) {
		return errors.New("库存不足")
	}
	// 更新
	lastInventoryCount := inventoryEntity.InventoryCount.Sub(changeCount)
	if _, err = tx.SpuInventoryEntity.WithContext(ctx).Where(
		dao.SpuInventoryEntity.ID.Eq(inventoryEntity.ID)).UpdateSimple(
		dao.SpuInventoryEntity.InventoryCount.Value(lastInventoryCount),
	); err != nil {
		return err
	}
	return nil
}

func (s _spuInventoryService) GetSpuInventoryApi(ctx context.Context, enterpriseId int64, spuId int64, warehouseID int64) decimal.Decimal {
	inventoryEntity, err := dao.SpuInventoryEntity.WithContext(ctx).Where(
		dao.SpuInventoryEntity.EnterpriseID.Eq(enterpriseId),
		dao.SpuInventoryEntity.SpuID.Eq(spuId),
		dao.SpuInventoryEntity.WarehouseID.Eq(warehouseID),
	).First()
	if err != nil {
		return decimal.Zero
	}
	return inventoryEntity.InventoryCount
}

func (s _spuInventoryService) GetAllSpuInventoryApi(ctx context.Context, enterpriseId int64, spuId int64) decimal.Decimal {
	inventoryEntity, err := dao.SpuInventoryEntity.WithContext(ctx).Where(
		dao.SpuInventoryEntity.EnterpriseID.Eq(enterpriseId),
		dao.SpuInventoryEntity.SpuID.Eq(spuId),
	).Find()
	if err != nil {
		return decimal.Zero
	}
	var inventoryCount = decimal.Zero
	for _, item := range inventoryEntity {
		inventoryCount = inventoryCount.Add(item.InventoryCount)
	}
	return inventoryCount
}

func (s _spuInventoryService) SpuInventoryBySpuIdListAPi(ctx context.Context, spuIdList []int64, warehouseID int64) map[int64]decimal.Decimal {
	tx := dao.SpuInventoryEntity.WithContext(ctx).Where(dao.SpuInventoryEntity.SpuID.In(spuIdList...))
	if warehouseID > 0 {
		tx = tx.Where(dao.SpuInventoryEntity.WarehouseID.Eq(warehouseID))
	}
	spuInventoryEntities, err := tx.Find()
	if err != nil || len(spuInventoryEntities) == 0 {
		return make(map[int64]decimal.Decimal)
	}
	spuInventoryMap := k.GroupBy(spuInventoryEntities, func(item *model.SpuInventoryEntity) int64 {
		return item.SpuID
	})
	var resultMal = make(map[int64]decimal.Decimal)
	for spuId, list := range spuInventoryMap {
		currentInventory := decimal.Zero
		for _, item := range list {
			currentInventory = currentInventory.Add(item.InventoryCount)
		}
		resultMal[spuId] = currentInventory
	}
	return resultMal
}

var SpuInventoryService = new(_spuInventoryService)
